/*
 * Copyright 2012 yura.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.opu.db.obj.vis.domain.dao.impl;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.io.IOUtils;
import org.opu.db.obj.vis.domain.dao.ObjectEntryDao;
import org.opu.db.obj.vis.domain.domains.ObjectEntry;
import org.opu.db.obj.vis.domain.domains.ObjectId;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;
import org.simpleframework.xml.stream.Format;

/**
 *
 * @author yura
 */
public class ObjectEntryDaoImpl implements ObjectEntryDao {
    
    public static final String FORMAT = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
    
    protected final Serializer serializer = new Persister(new Format(FORMAT));
    private final StorageFactory storageFactory;

    public ObjectEntryDaoImpl(StorageFactory storageFactory) {
        this.storageFactory = storageFactory;
    }
    
    @Override
    public ObjectEntry get(ObjectId id) {
        List<ObjectEntry> list = getList();
        for (ObjectEntry objectEntry : list) {
            if (objectEntry.getId().equals(id)) {
                return objectEntry;
            }
        }
        return null;
    }

    @Override
    public List<ObjectEntry> getList() {
        InputStream inputStream = null;
        try {
            inputStream = storageFactory.getInputStream();
            ObjectEntryList read = serializer.read(ObjectEntryList.class, inputStream);
            return read.getEntrys();
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        } finally {
            IOUtils.closeQuietly(inputStream);
        }
    }

    @Override
    public ObjectId save(ObjectEntry object) {
        saveOrUpdate(object);
        return object.getId();
    }

    @Override
    public void saveAll(List<ObjectEntry> list) {
        OutputStream outputStream = null;
        try {
            outputStream = storageFactory.getOutputStream();
            serializer.write(new ObjectEntryList(list), outputStream);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        } finally {
            IOUtils.closeQuietly(outputStream);
        }
    }

    @Override
    public void saveOrUpdate(final ObjectEntry object) {
        modifyEntry(REMOVE_CALLBACK, new CallBack<List<ObjectEntry>>() {

            @Override
            public void react(List<ObjectEntry> t) {
                t.add(object);
            }
        }, object);
    }

    @Override
    public void delete(ObjectEntry object) {
        modifyEntry(REMOVE_CALLBACK, EMPTY_CALLBACK, object);
    }
    
    void modifyEntry(CallBack<Iterator<ObjectEntry>> iteratorCb, 
            CallBack<List<ObjectEntry>> listCb, ObjectEntry object) {
        List<ObjectEntry> list = getList();
        for (Iterator<ObjectEntry> it = list.iterator(); it.hasNext();) {
            ObjectEntry objectEntry = it.next();
            if (objectEntry.equals(object)) {
                iteratorCb.react(it);
                break;
            }
        }
        
        listCb.react(list);
        
        try {
            OutputStream outputStream = storageFactory.getOutputStream();
            serializer.write(new ObjectEntryList(list), outputStream);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }
    
    static interface CallBack<T> {
        void react(T t) ;
    }
    
    static final CallBack<List<ObjectEntry>> EMPTY_CALLBACK 
            = new CallBack<List<ObjectEntry>>() {
        
        @Override public void react(List<ObjectEntry> t) {
        }
    };
    
    static final CallBack<Iterator<ObjectEntry>> REMOVE_CALLBACK 
            = new CallBack<Iterator<ObjectEntry>>() {
        
        @Override public void react(Iterator<ObjectEntry> t) {
            t.remove();
        }
    };
    
    
}
